
package com.hk.web.core.credentials;

import com.hk.base.constant.BaseConstant;
import com.hk.commons.redis.RedisUtil;
import com.hk.commons.utils.SpringContexUtil;
import com.hk.dao.manage.user.vo.ManagerUserLoginVO;
import com.hk.manage.login.enums.UserErrorEnum;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AccountException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;

/**
 * Shiro-密码输入错误的状态下重试次数的匹配管理
 */
public class RetryLimitCredentialsMatcher extends CredentialsMatcher {

    /**
     * 用户登录次数计数  redisKey 前缀
     */
    private static final String SHIRO_LOGIN_COUNT = "hk:shiro:user:loginCount:";
    /**
     * 用户登录是否被锁定    一小时 redisKey 前缀
     */
    private static final String SHIRO_IS_LOCK = "hk:shiro:user:loginLock:";

    @Override
    public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
        ManagerUserLoginVO user = (ManagerUserLoginVO) info.getPrincipals().getPrimaryPrincipal();
        String username = user.getUsername();
        //从容器中获取 redis
        RedisUtil redisUtil = SpringContexUtil.getBean(RedisUtil.class);
        // 访问一次，计数一次
        String isLockKey = SHIRO_IS_LOCK + username;
        if (redisUtil.hasKey(isLockKey)) {
            throw new AccountException(UserErrorEnum.LOGIN_LOCK_ENUM.getCode());
        }
        boolean matches = super.doCredentialsMatch(token, info);
        String loginCountKey = SHIRO_LOGIN_COUNT + username;
        if (!matches) {
            Object value = redisUtil.getValue(loginCountKey);
            long increment = 1;
            if (null == value) {
                //1分钟输入错误5次
                redisUtil.setObj(loginCountKey, increment, 60L);
            } else {
                increment = redisUtil.increment(loginCountKey);
            }
            //连续输入错误5次锁定用户 5分钟
            if (increment > 5) {
                redisUtil.setString(isLockKey, "lock", 300L);
                redisUtil.delKey(loginCountKey);
                throw new AccountException(UserErrorEnum.LOGIN_LOCK_ENUM.getCode());
            }
            throw new AccountException(UserErrorEnum.LOGIN_ERROR_ENUM.getCode());
        }
        //清空登录计数
        redisUtil.delKey(loginCountKey);
        // 注：User必须实现序列化
        Subject subject = SecurityUtils.getSubject();
        Session session = subject.getSession();
        session.setTimeout(900000L);//设置过期时间
        session.setAttribute(BaseConstant.LOGIN_SESSION_USER, user);
        return true;
    }
}
